package com.ld.shieldsb.db.service;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import com.ld.shieldsb.common.core.model.Result;
import com.ld.shieldsb.common.core.util.StringUtils;
import com.ld.shieldsb.db.dao.dynamic.DynamicBaseDao;
import com.ld.shieldsb.db.dao.dynamic.DynamicDaoFactory;
import com.ld.shieldsb.db.model.DB;
import com.ld.shieldsb.db.model.DBSetInfo;
import com.ld.shieldsb.db.model.DBTableColumnModel;
import com.ld.shieldsb.db.model.DBTableModel;
import com.ld.shieldsb.db.service.impl.MysqlTabelService;
import com.ld.shieldsb.db.service.impl.OracleTabelService;

import lombok.extern.slf4j.Slf4j;

/**
 * table转model服务
 * 
 * @ClassName Tabel2ModelService
 * @author 朱鹏程
 * @date 2016年8月23日 上午9:23:00
 *
 */
@Slf4j
public abstract class DBTabelService {
    protected DBSetInfo dbSetInfo;

    public DBTabelService(DBSetInfo dbSetInfo) {
        init(dbSetInfo);
    }

    protected void init(DBSetInfo dbSetInfo) {
        this.dbSetInfo = dbSetInfo;
    }

    /**
     * 加载DB服务类
     * 
     * @Title loadDBTableService
     * @author 吕凯
     * @date 2016年8月25日 下午5:33:27 void
     */
    public static DBTabelService getInstance(DBSetInfo dbSetInfo) {
        DBTabelService dbTableService = null;
        if (dbSetInfo != null && StringUtils.isNotEmpty(dbSetInfo.getDbUrl())) {
            String dbType = getDBType(dbSetInfo);
            if (DB.ORACLE.equals(dbType)) {
                dbTableService = new OracleTabelService(dbSetInfo);
            } else if (DB.MYSQL.equals(dbType)) {
                dbTableService = new MysqlTabelService(dbSetInfo);
            } else { // 其他默认使用Oracle
                log.error("没有匹配的数据库操作服务");
                dbTableService = new OracleTabelService(dbSetInfo);
            }
        }
        return dbTableService;
    }

    /**
     * 获取数据库类型
     * 
     * @Title getDBType
     * @author 吕凯
     * @date 2020年4月9日 下午3:33:44
     * @param dbSetInfo
     * @return String
     */
    public static String getDBType(DBSetInfo dbSetInfo) {
        String dbUrl = dbSetInfo.getDbUrl();
        String dbType = DB.getDBType(dbUrl);
        return dbType;
    }

    /**
     * 数据库中字段类型转java类型
     * 
     * @Title oracleType2JavaType
     * @author 吕凯
     * @date 2016年8月25日 上午10:52:12
     * @param sqlType
     * @param scale
     *            小数位数
     * @param size
     *            长度
     * @return String
     */
    public static String dbType2JavaType(String sqlType, int scale, int size) {
        if (sqlType != null) {
            sqlType = sqlType.toLowerCase();
            if (sqlType.equals("integer") || sqlType.equals("int") || sqlType.equals("tinyint") || sqlType.equals("bit")) {
                return "Integer";
            } else if (sqlType.equals("long") || sqlType.equals("bigint")) {
                return "Long";
            } else if (sqlType.equals("float") || sqlType.equals("float precision") || sqlType.equals("double")
                    || sqlType.equals("double precision")) {
                return "Double";
            } else if (sqlType.equals("number") || sqlType.equals("decimal") || sqlType.equals("numeric") || sqlType.equals("real")) {
                return scale == 0 ? (size < 10 ? "Integer" : "Long") : "Double";
            } else if (sqlType.equals("varchar") || sqlType.equals("varchar2") || sqlType.equals("char") || sqlType.equals("nvarchar2")
                    || sqlType.equals("nchar") || sqlType.equals("text") || sqlType.equals("longtext") || sqlType.equals("json")
                    || sqlType.equals("clob")) {
                return "String";
            } else if (sqlType.equals("datetime") || sqlType.equals("date") || sqlType.equals("timestamp")) {
                return "Date";
            }
        }
        return null;
    }

    /**
     * 根据表名返回列信息（通用，部分不兼容的数据库可重写）<br>
     * REMARKS获取不到的可以设置：<br>
     * Properties properties = new Properties(); <br>
     * properties.setProperty("remarks", "true");<br>
     * properties.setProperty("useInformationSchema", "true");
     * 
     * @Title getTableColumn
     * @author 吕凯
     * @date 2016年8月25日 上午10:12:06
     * @param tableName
     * @return List<DBTableColumnModel>
     */
    public List<DBTableColumnModel> getTableColumn(String tableName) {
        List<DBTableColumnModel> tableList = new ArrayList<>();
        ResultSet colRet = null;
        Properties props = new Properties();
        props.put("remarksReporting", "true");// 注意这里
        props.put("user", dbSetInfo.getDbUserName());
        props.put("password", dbSetInfo.getDbPassword());
        try (Connection conn = DriverManager.getConnection(dbSetInfo.getDbUrl(), props)) {
            DatabaseMetaData dbmd = conn.getMetaData();
            colRet = dbmd.getColumns(null, dbSetInfo.getDbUserName().toUpperCase(), tableName, "%");
            while (colRet.next()) {
                String columnName = colRet.getString("COLUMN_NAME");
                String remarks = colRet.getString("REMARKS");
                String dataType = colRet.getString("TYPE_NAME");
                int datasize = colRet.getInt("COLUMN_SIZE");
                int digits = colRet.getInt("DECIMAL_DIGITS");
                int nullable = colRet.getInt("NULLABLE"); // 返回1就表示可以是Null,而0就表示Not Null。
                DBTableColumnModel tableColumn = new DBTableColumnModel();
                tableColumn.setTableName(tableName);
                tableColumn.setColumnName(columnName);
                tableColumn.setDataType(dataType);
                tableColumn.setDatasize(datasize);
                tableColumn.setDigits(digits);
                tableColumn.setRemarks(remarks);
                tableColumn.setNullable(nullable == 1 ? true : false);
                String columnType = dataType;
                if (datasize > 0) {
                    String addStr = datasize + "";
                    if (digits > 0) {
                        addStr += "," + digits;
                    }
                    columnType += "(" + addStr + ")";
                }
                tableColumn.setColumnType(columnType);
                tableList.add(tableColumn);
            }

        } catch (Exception e) {
            log.error("查询用户" + dbSetInfo.getDbUserName() + "下的表" + tableName + "出错！", e);
        } finally {
            if (colRet != null) {
                try {
                    colRet.close();
                } catch (SQLException e) {
                    log.error("关闭出错！", e);
                }
            }

        }
        return tableList;
    }

    /**
     * 测试连接是否正常（通用，部分不兼容的数据库可重写）
     * 
     * @Title testCon
     * @author 吕凯
     * @date 2016年8月29日 上午9:15:24
     * @param url
     * @param userName
     * @param password
     * @return boolean
     */
    public static Result testCon(String url, String userName, String password) {
        Result result = new Result();
        result.setSuccess(false);
        if (StringUtils.isBlank(url)) {
            result.setMessage("url为空");
            return result;
        }
        if (StringUtils.isBlank(userName)) {
            result.setMessage("用户名为空");
            return result;
        }
        if (StringUtils.isBlank(password)) {
            result.setMessage("密码为空");
            return result;
        }
        String strsql = " SELECT 1 FROM dual ";
        PreparedStatement pstmt = null;
        try (Connection conn = DriverManager.getConnection(url, userName, password)) {
            pstmt = conn.prepareStatement(strsql);
            pstmt.executeQuery();
            result.setSuccess(true);
        } catch (Exception e) {
            String errMsg = e.getMessage();
            if (errMsg.contains("No suitable driver")) {
                result.setMessage("缺少驱动程序！");
            } else if (errMsg.contains("Unknown database")) { // Table 'test' already exists Query: CREATE TABLE……
                errMsg = "库 " + StringUtils.substringAfter(errMsg, "Unknown database") + "不存在！";
                result.setMessage(errMsg);
            } else if (errMsg.contains("The driver has not received any packets from the server")) {
                result.setMessage("无响应，请检查配置参数是否正确！");
            }
            log.error("数据库连接出错" + url + " " + userName + " " + password + "！", e);
        } finally {
            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    log.error("关闭出错！", e);
                }
            }

        }
        return result;
    }

    /**
     * 获取table信息（子类实现）
     * 
     * @Title getTables
     * @author 吕凯
     * @date 2017年3月24日 下午5:29:26
     * @param tableNameKey
     *            表名关键字
     * @return List<DBTableModel>
     */
    public abstract List<DBTableModel> getTables(String tableNameKey);

    /**
     * 检查表是否存在
     * 
     * @Title checkTable
     * @author 吕凯
     * @date 2019年10月31日 下午4:37:46
     * @param tableNameKey
     * @return Result
     */
    public abstract Result checkTableExist(String tableName);

    /**
     * 获取添加列的语句
     * 
     * @Title getAddColumnSql
     * @author 吕凯
     * @date 2018年9月12日 下午5:44:05
     * @param col
     * @param beforeCol
     * @return String
     */
    public abstract String getAddColumnSql(DBTableColumnModel col, DBTableColumnModel beforeCol);

    /**
     * 获取删除列的语句（通用方法，不符合具体数据库的可覆盖）
     * 
     * @Title getDropColumnSql
     * @author 吕凯
     * @date 2018年9月12日 下午5:29:23
     * @param col
     * @return
     * @see com.ld.util.dbcompare.DBTabelService#getDropColumnSql(com.ld.util.ExportTableColumnModel.model.DBTableColumnModel)
     */
    public String getDropColumnSql(DBTableColumnModel col) {
        return "ALTER TABLE " + col.getTableName() + " DROP COLUMN " + col.getColumnName() + "";
    }

    /**
     * 获取修改列的语句
     * 
     * @Title getModifyColumnSql
     * @author 吕凯
     * @date 2018年9月12日 下午5:44:22
     * @param col
     * @param beforeCol
     * @return String
     */
    public abstract String getModifyColumnSql(DBTableColumnModel col, DBTableColumnModel beforeCol);

    /**
     * 获取创建表的语句（表存在时）
     * 
     * @Title getCreateTableSql
     * @author 吕凯
     * @date 2018年9月12日 下午5:44:32
     * @param tableModel
     * @return String
     */
    public abstract String getCreateTableSql(String tableName);

    /**
     * 根据dbTableModel拼接出建表语句
     * 
     * @Title getCreateTableSql
     * @author 吕凯
     * @date 2019年1月23日 上午10:53:27
     * @param tableModel
     * @return String
     */
    public abstract String getCreateTableSql(DBTableModel tableModel);

    /**
     * 
     * 根据dbTableModel建表
     * 
     * @Title createTable
     * @author 吕凯
     * @date 2020年4月7日 上午9:05:45
     * @param tableModel
     * @return Result
     */
    public Result createTable(DBTableModel tableModel) {
        String createTableStr = getCreateTableSql(tableModel);
        Result result = createTable(createTableStr);
        return result;
    }

    /**
     * 
     * 根据建表语句创建表
     * 
     * @Title createTable
     * @author 吕凯
     * @date 2020年4月7日 上午9:20:37
     * @param createTableStr
     * @return Result
     */
    public abstract Result createTable(String createTableStr);

    /**
     * 获取删除表的语句（通用方法，不符合具体数据库的可覆盖）
     * 
     * @Title getDropTableSql
     * @author 吕凯
     * @date 2018年9月12日 下午5:30:29
     * @param tableModel
     * @return String
     */
    public String getDropTableSql(DBTableModel tableModel) {
        return "DROP TABLE " + tableModel.getTableName();
    }

    /**
     * 
     * 获取删除表的语句（通用方法，不符合具体数据库的可覆盖）
     * 
     * @Title getDropTableSql
     * @author 吕凯
     * @date 2019年1月23日 上午10:52:52
     * @param tableName
     * @return String
     */
    public String getDropTableSql(String tableName) {
        return "DROP TABLE " + tableName;
    }

    /**
     * 返回DAO对象
     * 
     * @Title getDAO
     * @author 吕凯
     * @date 2019年4月26日 下午1:41:13
     * @return DynamicBaseDao
     */
    public DynamicBaseDao getDAO() {
        DynamicBaseDao baseDao = DynamicDaoFactory.getDynamicDao(dbSetInfo.getDbUrl(), dbSetInfo.getDbUserName(),
                dbSetInfo.getDbPassword());
        return baseDao;
    }

    public Result execSql(DynamicBaseDao DAO, String sql, Object... params) {
        Result result = new Result();
        result.setSuccess(false);
        boolean flag = false;
        String msg = "";
        try {
            flag = DAO.execSql(sql, params);
            result.setSuccess(flag);
        } catch (Exception e) {
            log.error("执行sql出错！", e);
            String errMsg = e.getMessage();
            if (errMsg.contains("already exists Query")) { // Table 'test' already exists Query: CREATE TABLE……
                errMsg = "表 " + StringUtils.substringBetween(errMsg, "Table '", "' already exists") + " 已经存在！";
            }
            result.setMessage(errMsg);
            return result;
        }
        if (flag) {
            result.setSuccess(true);
            result.setMessage("执行成功！");
            return result;
        } else {
            result.setMessage(msg);
            return result;
        }
    }

}
